package cn.yangliu.nacos.comm.config.security.handler;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import cn.yangliu.nacos.comm.config.security.annotations.Authorize;
import cn.yangliu.nacos.comm.config.security.enums.Logic;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

/**
 * The interface Authority aspect.
 *
 * @author 问道于盲
 */
@Aspect
@Order(Integer.MIN_VALUE)
public class AuthorityAspect {

    /**
     * current class instance's member.
     * The Authorize handler.
     */
    @Autowired(required = false)
	private AuthorityHandler authorityHandler;

    /**
     * Point cut.
     */
    @Pointcut("@annotation(cn.yangliu.nacos.comm.config.security.annotations.Authorize)")
	public void pointCut() {

	}

    /**
     * Proceed object.
     *
     * @param joinPoint joinPoint
     * @return Object object
     * @throws Throwable throwable
     */
    @Around("pointCut()")
	public Object proceed(ProceedingJoinPoint joinPoint) throws Throwable {
		try {

			Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
			// 用户权限
			List<String> userAuthorizes = authorityHandler.findAuthorities(authentication);
			if (userAuthorizes.isEmpty()) {
				throw new AccessDeniedException("you don't have any permission");
			}

			Authorize authorize = getMethodAnnotation(joinPoint);
			// 校验逻辑
			Logic logic = authorize.logic();

			// 需要鉴定的权限
			List<String> authorizes = Arrays.asList(authorize.value());

			boolean hasPermission = false;
			// 需要同时包含多个权限的校验
			if (Objects.equals(logic, Logic.AND)) {
				hasPermission = userAuthorizes.containsAll(authorizes);
			}

			// 只需要满足一个权限的校验
			if (Objects.equals(logic, Logic.OR)) {
				for (String auth : authorizes) {
					if (userAuthorizes.contains(auth)) {
						hasPermission = true;
						break;
					}
				}
			}

			//无权限抛出AccessDeniedException异常
			if (!hasPermission) {
				throw new AccessDeniedException("access denied");
			}
			return joinPoint.proceed();

		} catch (Exception e) {
			throw e;
		}
	}

    /**
     * current class method for getter.
     * Gets method annotation.
     *
     * @param joinPoint the join point
     * @return the method annotation
     */
    private Authorize getMethodAnnotation(ProceedingJoinPoint joinPoint) {
		MethodSignature signature = (MethodSignature)joinPoint.getSignature();
		Method method = signature.getMethod();
		return method.getDeclaredAnnotation(Authorize.class);
	}
}
